//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include <_hal.h>
#include "setup.h"

TaskStateSegment g_tss;

STATIC DECL_ALIGN(8) SegmentDescriptor g_gdt[NumberOfGdtEntries] = {
    // No.0 -- Null
    {
        0x0000, 0x0000, 0x00, 0x00, 0x0, 0x0, 0x00
    },

    // No.1 -- 4GB readable code segment for kernel
    {
        0xffff, 0x0000, 0x00,
        SegDescFlag_Present + SegDescFlag_DPL0 + SegDescFlag_Segment
            + SegDescFlag_Type_Executable + SegDescFlag_Type_Readable,
        0xf,
        SegDescFlag_Granularity + SegDescFlag_DB,
        0x00
    },

    // No.2 -- 4GB writable data segment for kernel
    {
        0xffff, 0x0000, 0x00,
        SegDescFlag_Present + SegDescFlag_DPL0 + SegDescFlag_Segment
            + SegDescFlag_Type_Writable,
        0xf,
        SegDescFlag_Granularity + SegDescFlag_DB,
        0x00
    },

    // No.3 -- 4GB readable code segment for user
    {
        0xffff, 0x0000, 0x00,
        SegDescFlag_Present + SegDescFlag_DPL3 + SegDescFlag_Segment
            + SegDescFlag_Type_Executable + SegDescFlag_Type_Readable,
        0xf,
        SegDescFlag_Granularity + SegDescFlag_DB,
        0x00
    },

    // No.4 -- 4GB writable data segment for user
    {
        0xffff, 0x0000, 0x00,
        SegDescFlag_Present + SegDescFlag_DPL3 + SegDescFlag_Segment
            + SegDescFlag_Type_Writable,
        0xf,
        SegDescFlag_Granularity + SegDescFlag_DB,
        0x00
    },

    // No.5 -- Tss
    //  gcc bug !
//#define TSS_LIMIT  (sizeof(TaskStateSegment) - 1)
//    {
//        LOWORD(TSS_LIMIT),
//        LOWORD(&g_tss),
//        LOBYTE(HIWORD(&g_tss)),
//        SegDescFlag_Present + SegDescFlag_DPL0 + SegDescFlag_Type_TSS,
//        LONIBBLE(HIWORD(TSS_LIMIT)),
//        0,
//        HIBYTE(HIWORD(&g_tss))
//    },
    {
        0x0000, 0x0000, 0x00, 0x00, 0x0, 0x0, 0x00
    },
};

EXTERN GateDescriptor g_idt[c_uNumberOfIdtEntries];

DECL_ALIGN(8) GateDescriptor g_idt[c_uNumberOfIdtEntries];

EXTERN DECL_CODEINIT void CDECL SetupSegmentation()
{
    // Setup gdt
    GdtPointer gdtPointer = { sizeof(g_gdt) - 1, (uint32_t)g_gdt };

#define TSS_LIMIT   (sizeof(TaskStateSegment) - 1)

    g_gdt[SelectorIndex_TSS].u16Limit_0_15  = LOWORD(TSS_LIMIT);
    g_gdt[SelectorIndex_TSS].u16Base_0_15   = LOWORD(&g_tss);
    g_gdt[SelectorIndex_TSS].u8Base_16_23   = LOBYTE(HIWORD(&g_tss));
    g_gdt[SelectorIndex_TSS].u8Flag_0_15    =
        SegDescFlag_Present + SegDescFlag_DPL0 + SegDescFlag_Type_TSS;
    g_gdt[SelectorIndex_TSS].u4Limit_19_16  = LONIBBLE(HIWORD(TSS_LIMIT));
    g_gdt[SelectorIndex_TSS].u4Flag_16_23   = 0;
    g_gdt[SelectorIndex_TSS].u8Base_24_31   = HIBYTE(HIWORD(&g_tss));

    ASM("lgdt   %0;": :"m"(gdtPointer));

    // Setup idt
    IdtPointer idtPointer = { sizeof(g_idt) - 1, (uint32_t)g_idt };
    ASM("lidt   %0;": :"m"(idtPointer));

    // Set tr
    g_tss.ss0 = KERNEL_DATA_SELECTOR;

    ASM("ltr    %%ax;": :"a"(TSS_SELECTOR));

    // Clear ldt
    ASM("movw   $0, %ax;"
        "lldt   %ax;");

    // Update segment registers: ds, ss, es, cs
    ASM("movw   %%ax, %%ds;"
        "movw   %%ax, %%ss;"
        "movw   %%ax, %%es;"
        : :"a"(KERNEL_DATA_SELECTOR));

    ASM("ljmp   %0, $__SetupSegmentation_Next;"
        "__SetupSegmentation_Next:"
        : :"i"(KERNEL_CODE_SELECTOR));

    // Clear fs and gs
    ASM("movw   %%ax, %%fs;"
        "movw   %%ax, %%gs;"
        : :"a"(NULL_SELECTOR));
}

EXTERN void CDECL SetGlobalDescriptor(uint_t uIndex,
    SegmentDescriptor *pDescriptor)
{
    g_gdt[uIndex] = *pDescriptor;
}

EXTERN void SetGate(vector_t vector, uint16_t u16Flag, virtaddr_t va)
{
    g_idt[vector].u16Offset_0_15        = LOWORD((int)va);
    g_idt[vector].u16SegmentSelector    = KERNEL_CODE_SELECTOR;
    g_idt[vector].u16Flag               = u16Flag;
    g_idt[vector].u16Offset_16_31       = HIWORD((int)va);
}
